using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Extensions.FileProviders;
using SharpSoft.Data;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
using System.Security.Claims;
using Microsoft.AspNetCore.Mvc.Filters;
using System.Linq;
using System.Linq.Expressions;
using System.Text.RegularExpressions;
using Microsoft.Extensions.Options;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Mvc.Controllers;
using System.Diagnostics;

namespace SharpSoft.Web
{
    using Microsoft.Extensions.Logging;
    using Microsoft.Extensions.Primitives;
    using Newtonsoft.Json.Linq;
    using System.IO;
    using System.Runtime.Serialization;

    /// <summary>
    /// 扩展带视图功能的控制器，作为框架的控制器基类。提供一系列便捷的扩展功能
    /// </summary>
    public abstract class BaseController : Controller
    {

        #region CONSTS
        /// <summary>
        /// 将验证码内容存入Session中的Key
        /// </summary>
        public const string ValCode_SessionValueKey = "__valcode";
        #endregion

        /// <summary>
        /// 获取系统数据库对象
        /// </summary>
        protected Database db { get; private set; }
        /// <summary>
        /// HttpContext访问器
        /// </summary>
        protected readonly IHttpContextAccessor ContextAccessor;
        /// <summary>
        /// 宿主环境
        /// </summary>
        protected readonly IWebHostEnvironment HostingEnvironment;
        /// <summary>
        /// 应用配置
        /// </summary>
        protected readonly AppConfig AppConfig;

        protected readonly ILogger<BaseController> logger;
        public BaseController()
        {
            AppConfig = GetService<IOptions<AppConfig>>().Value;
            ContextAccessor = GetService<IHttpContextAccessor>();
            HostingEnvironment = GetService<IWebHostEnvironment>();
            db = GetService<DatabaseService>().DefaultDatabase;
            logger = GetService<ILogger<BaseController>>();
        }
      

        #region Inject
        /// <summary>
        /// 获取注册的服务实例
        /// </summary>
        /// <param name="serviceType"></param>
        /// <returns></returns>
        public Object GetService(Type serviceType)
        {
            return ServiceLocator.Instance.GetService(serviceType);
        }
        /// <summary>
        /// 获取注册的服务实例
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T GetService<T>() where T : class
        {
            return ServiceLocator.Instance.GetService(typeof(T)) as T;
        }
        #endregion

        #region Helper

        /// <summary>
        /// 快速获取数据访问层对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        protected T dao<T>() where T : SharpSoft.Data.DAOS.DAO
        {
            var type = typeof(T);
            var ctor = type.GetConstructor(new Type[] { typeof(Database), typeof(int) });
            if (ctor == null)
            {
                throw new Exception($"找不到数据层类型[{type.FullName}]的相关构造函数。");
            }
            return (T)ctor.Invoke(new object[] { this.db, this.UserId });
        }
        /// <summary>
        /// 获取远程客户端的IP地址
        /// </summary>
        /// <returns></returns>
        protected System.Net.IPAddress GetClientIp()
        {
            return ContextAccessor.HttpContext.Connection.RemoteIpAddress;
        }  /// <summary>
           /// 获取相对于wwwroot的绝对路径
           /// </summary>
           /// <param name="paths"></param>
           /// <returns></returns>
        public string MapPath(params string[] paths)
        {
            List<string> l = new List<string>(paths.Length + 1);
            l.Add(HostingEnvironment.WebRootPath);
            l.AddRange(paths);
            return System.IO.Path.Combine(l.ToArray());
        }
        /// <summary>
        /// 获取相对于app根目录的绝对路径
        /// </summary>
        /// <param name="paths"></param>
        /// <returns></returns>
        public virtual string MapAppPath(params string[] paths)
        {
            List<string> l = new List<string>(paths.Length + 1);
            l.Add(this.HostingEnvironment.ContentRootPath);
            l.AddRange(paths);
            return System.IO.Path.Combine(l.ToArray());
        }
        #endregion

        #region Timelife

        ActionExecutingContext actionContext;

        public override void OnActionExecuting(ActionExecutingContext context)
        {
            actionContext = context;
            if (db != null)
            {//使用默认数据连接的副本
                db = db.Clone();
            }
            //if (context.ActionDescriptor is ControllerActionDescriptor cad)
            //{
            //    var mi = cad.MethodInfo;
            //    var atts = mi.GetCustomAttributes(typeof(PageTitleAttribute), true);
            //    if (atts.Length > 0)
            //    {
            //        var pt = (PageTitleAttribute)atts.Last();
            //        this.ViewBag.Title = pt.Title;
            //    }
            //}

            base.OnActionExecuting(context);
        }

        public override void OnActionExecuted(ActionExecutedContext context)
        {
            //if (context.Exception != null && !context.ExceptionHandled)
            //{
            //    logger.LogError(context.Exception, "未处理的异常。");
            //}
            if (db != null)
            {
                try
                {
                    db.EnsureTransactionSafe();//在Action执行完成后确保数据事务已正确处理 
                }
                catch (Exception ex)
                {
                    context.Exception = ex;
                    ConsoleHelper.Danger($"数据事务未正确处理：{context.ActionDescriptor.DisplayName}");
#if DEBUG
                    Debug.Fail("数据事务未正确处理。");
#endif 
                }
                db.Close();
                db.Dispose();
                db = null;
            }
            base.OnActionExecuted(context);
        }
        protected override void Dispose(bool disposing)
        {
            if (db != null)
            {
                db.Close();
                db.Dispose();
            }
            base.Dispose(disposing);
        }
        #endregion

        #region 登录信息

        /// <summary>
        /// 获取当前登录的用户身份信息
        /// </summary>
        public AppUser AppUser
        {
            get
            {
                return this.HttpContext.GetAppUser();
            }
        }

        public int UserId
        {
            get
            {
                var user = this.AppUser;
                if (user == null)
                {
                    return -1;
                }
                return user.UserId;
            }
        }
        /// <summary>
        /// 使用户登录（在Cookies记录用户信息）
        /// </summary>
        /// <param name="user"></param>
        protected async Task SignInAsync(AppUser user, bool cookieIsPersistent)
        {
            await this.HttpContext.SignInAsync(user, cookieIsPersistent);
        }
        /// <summary>
        /// 使用户退出登录
        /// </summary>
        protected async Task SignOutAsync()
        {
            await this.HttpContext.SignOutAsync();
        }
        /// <summary>
        /// 检测图形验证码是否匹配
        /// </summary>
        /// <param name="valcode"></param>
        protected void MatchAuthCode(string valcode)
        {

            if (valcode == null)
            {
                valcode = "";
            }
            if (HttpContext.Session == null || !HttpContext.Session.IsAvailable)
            {//检查是否启用了Session
                throw new Exception(Properties.Resources.error_authcode_without_session);
            }
            var nowval = HttpContext.Session.GetString(ValCode_SessionValueKey);
            if (nowval == null)
            {
                nowval = "";
            }
            if (nowval.ToUpper() != valcode.ToUpper())
            {
                throw new Exception(Properties.Resources.error_authcode_notmatch);
            }
        }
        #endregion


        #region ActionResult扩展


        /// <summary>
        /// 返回表示成功的<see cref="ApiResult"/>
        /// </summary>
        /// <param name="data"></param>
        /// <param name="msg"></param>
        /// <returns></returns>
        protected ApiResult Success(object data = null, string msg = "")
        {
            return new ApiResult(1, "") { data = data, message = msg };
        }
        /// <summary>
        /// 返回表示失败的<see cref="ApiResult"/>
        /// </summary>
        /// <param name="status"></param>
        /// <param name="msg"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        protected ApiResult Fail(int status = -1, string msg = "", object data = null)
        {
            return new ApiResult(status, msg) { data = data };
        }
        #endregion

        #region 页面静态化

        /// <summary>
        /// 获取当前的路由信息
        /// </summary>
        /// <returns></returns>
        private string[] getRouteLink()
        {
            var routeList = new List<string>();
            var rvals = this.RouteData.Values.Values;
            foreach (var item in rvals)
            {
                routeList.Add(item.ToString());
            }
            var route = routeList.ToArray();
            return route;
        }

        /// <summary>
        /// 立即缓存当前页面
        /// </summary>
        /// <param name="model"></param>
        /// <param name="viewName"></param>
        /// <param name="isPartialView"></param>
        /// <returns></returns>
        protected async Task CachePage(object model, string viewName = null, bool isPartialView = false)
        {
            var StaticizeService = GetService<StaticizeService>();
            var content = await StaticizeService.GetViewRenderResult(this, viewName, model, isPartialView);
            StaticizeService.CachePage(getRouteLink(), content);
        }
        /// <summary>
        /// 返回带静态化功能的视图
        /// </summary>
        /// <param name="viewName">视图名称，为null表示使用当前Action上下文的默认视图。</param>
        /// <param name="fun_loadModel">提供一个函数，用于加载视图模型。</param>
        /// <param name="isPartialView">是否为分部视图</param>
        /// <returns></returns>
        protected async Task<IActionResult> StaticizeView(string viewName, Func<object> fun_loadModel = null, bool isPartialView = false)
        {
            var StaticizeService = GetService<StaticizeService>();
            if (StaticizeService == null)
            {
                throw new Exception(Properties.Resources.error_staticize_without_service);
            }
            object model = fun_loadModel?.Invoke();
            if (model == null)
            {
                model = this.ViewData.Model;
            }

            var route = getRouteLink();
            var needcache = StaticizeService.GetNeedCache(route);
            if (needcache)
            {//缓存当前页面
                var content = await StaticizeHelper.GetViewRenderResult(this, viewName, model, isPartialView);
                StaticizeService.CachePage(route, content);
            }
            else
            {//直接读取缓存页面
                var content = StaticizeService.GetCachedPageContent(route);
                return Content(content, "text/html", Encoding.UTF8);
            }
            if (isPartialView)
            {
                return PartialView(viewName, model);
            }
            return View(viewName, model);
        }
        /// <summary>
        /// 返回带静态化功能的视图
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        protected async Task<IActionResult> StaticizeView(Func<object> fun_loadModel = null, bool isPartialView = false)
        {
            return await this.StaticizeView(null, fun_loadModel, isPartialView);
        }

        #endregion

        #region 数据检查
        /// <summary>
        /// 将请求的所有参数放入ViewData中，优先级[正文JSON数据]>[表单]>[文件数据]>[Url参数]。
        /// </summary>
        /// <param name="inModel">传入复杂类型的参数。默认为null。</param>
        protected void PutArgumentsToViewData(object inModel = null)
        {
            Dictionary<string, object> dic = new Dictionary<string, object>();
            var c = this.actionContext;
            if (c == null)
            {
                throw new Exception("Action上下文不存在，无法统计参数。");
            }
            var sf = new StackFrame(1);
            var method = sf.GetMethod();
            if (method == null)
            {
                throw new Exception("获取调用函数失败，无法统计参数。");
            }
            var paras = method.GetParameters();
            foreach (var p in paras)
            {
                if (c.ActionArguments.ContainsKey(p.Name))
                {
                    dic.Add(p.Name, c.ActionArguments[p.Name]);
                }
                else if (p.HasDefaultValue)
                {
                    dic.Add(p.Name, p.DefaultValue);
                }
                else
                {
                    dic.Add(p.Name, (p.ParameterType.IsValueType ? Activator.CreateInstance(p.ParameterType) : null));
                }

            }
            foreach (var item in dic)
            {
                if (!(item.Value is IFormFile))
                {
                    ViewData[item.Key] = item.Value;
                }
            }
            //优先写入模型数据
            if (inModel != null)
            {
                var type = inModel.GetType();
                var ps = type.GetProperties();
                foreach (var p in ps)
                {
                    var key = p.Name;
                    var value = p.GetValue(inModel);
                    ViewData[key] = value;
                }
            }

        }

        /// <summary>
        /// 将请求的所有参数放入JObject中，优先级[正文JSON数据]>[表单]>[文件数据]>[Url参数]。
        /// </summary>
        /// <param name="output">输出到的JObject</param>
        /// <param name="inModel">输入对象，如没有则为null</param>
        /// <remarks>仅在Action中调用有效</remarks>
        protected void PutArgumentsToJObject(JObject output)
        {
            if (output == null)
            {
                throw new ArgumentNullException(nameof(output));
            }
            Dictionary<string, object> dic = new Dictionary<string, object>();
            var c = this.actionContext;
            if (c == null)
            {
                throw new Exception("Action上下文不存在，无法统计参数。");
            }
            var sf = new StackFrame(1);
            var method = sf.GetMethod();
            if (method == null)
            {
                throw new Exception("获取调用函数失败，无法统计参数。");
            }
            var paras = method.GetParameters();
            foreach (var p in paras)
            {
                if (c.ActionArguments.ContainsKey(p.Name))
                {
                    dic.Add(p.Name, c.ActionArguments[p.Name]);
                }
                else if (p.HasDefaultValue)
                {
                    dic.Add(p.Name, p.DefaultValue);
                }
                else
                {
                    dic.Add(p.Name, (p.ParameterType.IsValueType ? Activator.CreateInstance(p.ParameterType) : null));
                }

            }
            foreach (var item in dic)
            {
                if (!(item.Value is IFormFile))
                {
                    output[item.Key] = new JValue(item.Value);
                }
            }
        }

        /// <summary>
        /// <seealso cref="PutArgumentsToJObject"/>
        /// </summary>
        /// <param name="output"></param>
        protected void PutArgumentsToObject(object output)
        {
            if (output == null)
            {
                throw new ArgumentNullException(nameof(output));
            }
            Dictionary<string, object> dic = new Dictionary<string, object>();
            var c = this.actionContext;
            if (c == null)
            {
                throw new Exception("Action上下文不存在，无法统计参数。");
            }
            var sf = new StackFrame(1);
            var method = sf.GetMethod();
            if (method == null)
            {
                throw new Exception("获取调用函数失败，无法统计参数。");
            }
            var paras = method.GetParameters();
            foreach (var p in paras)
            {
                if (c.ActionArguments.ContainsKey(p.Name))
                {
                    dic.Add(p.Name, c.ActionArguments[p.Name]);
                }
                else if (p.HasDefaultValue)
                {
                    dic.Add(p.Name, p.DefaultValue);
                }
                else
                {
                    dic.Add(p.Name, (p.ParameterType.IsValueType ? Activator.CreateInstance(p.ParameterType) : null));
                }

            }
            JObject jobj = new JObject();
            foreach (var item in dic)
            {
                if (!(item.Value is IFormFile))
                {
                    jobj[item.Key] = new JValue(item.Value);
                }
            }
            jobj.CopyTo(output);
        }

        /// <summary>
        /// 检查Action传入的参数，如果检查不通过则将异常信息写入ModelState。
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="memberExpression">一个表达式，该表达式反回要检查的参数。</param>
        /// <param name="check">用于检查参数是否符合条件的方法，如果不符合在该方法中抛出相应异常。</param>
        protected void CheckViewArgument<T>(Expression<Func<T>> memberExpression, Action<T> check)
        {
            var body = memberExpression.Body;
            var value = memberExpression.Compile()();
            string name = null;
            if (body is MemberExpression me)
            {
                name = me.Member.Name;
            }
            else if (body is MethodCallExpression mc)
            {
                if (mc.Arguments.Count == 1)
                {
                    var e = mc.Arguments[0];
                    if (e is ConstantExpression ce)
                    {
                        name = ce.Value as string;
                    }
                }

            }
            else
            {
            }
            if (string.IsNullOrEmpty(name))
            {
                throw new Exception("无法获取要检查的成员名称。表达式参数只能是成员表达式或索引访问表达式。");
            }
            try
            {
                check(value);
            }
            catch (Exception ex)
            {
                ViewData.ModelState.AddModelError(name, ex.Message);
            }
        }
        /// <summary>
        /// 使用正则表达式验证指定的数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="memberExpression"></param>
        /// <param name="regex"></param>
        protected void CheckViewArgument<T>(Expression<Func<T>> memberExpression, Regex regex, string memberDisplayName)
        {
            CheckViewArgument(memberExpression, value =>
            {

                if (value == null || !regex.IsMatch(value.ToString()))
                {
                    @throw($"[{memberDisplayName}]不符合规则。");
                }
            });
        }

        /// <summary>
        /// 抛出异常
        /// </summary>
        /// <param name="err"></param>
        protected void @throw(string err)
        {
            throw new Exception(err);
        }
        /// <summary>
        /// 插入模型错误
        /// </summary>
        /// <param name="msg"></param>
        protected void modelError(string msg)
        {
            this.ModelState.TryAddModelError(Guid.NewGuid().ToString(), msg);
        }
        #endregion
    }
}
